using System;
using System.Collections.Generic;
using System.Text;

namespace DeflateLib
{
    /// <summary>
    /// Compressed data codes storage. This is the result of the 
    /// work of a compressor.
    /// </summary>
    /// <see cref="ICompressor"/>
    public sealed class CompressedData
    {
        /// <summary>
        /// Codes and additional data. If code more than 256 it is followed by 
        /// length extra data, distance code and its extra. 
        /// In CodeBits will appear two lengths of the extra data.
        /// </summary>
        internal List<ushort> Codes = new List<ushort>();

        /// <summary>
        /// Contains Codes extra data length in bits.
        /// </summary>
        internal List<byte> CodeBits = new List<byte>();

        internal int LengthWritten;

        /// <summary>
        /// Creates compressed data storage.
        /// </summary>
        public CompressedData()
        {
            LengthWritten = 0;
        }

        /// <summary>
        /// Writes byte to the storage.
        /// </summary>
        /// <param name="b">The byte.</param>
        public void WriteByte(byte b)
        {
            Codes.Add(b);
            LengthWritten++;
        }

        /// <summary>
        /// Writes refered substring as DEFLATE algorithm requires.
        /// </summary>
        /// <param name="distance">Distance to the substring start.</param>
        /// <param name="length">Length of the substring.</param>
        public void WriteDistanceLength(int distance, int length)
        {
            if (distance < 1 || distance > DeflateUtils.MaxDistance) throw new ArgumentOutOfRangeException("distance");
            if (length < 3 || length > DeflateUtils.MaxLength) throw new ArgumentOutOfRangeException("length");

            int lengthCode, lengthBits, lengthBitsCount;
            DeflateUtils.EncodeLength(length, out lengthCode, out lengthBits, out lengthBitsCount);
            int distanceCode, distanceBits, distanceBitsCount;
            DeflateUtils.EncodeDistance(distance, out distanceCode, out distanceBits, out distanceBitsCount);

            Codes.Add((ushort)lengthCode);
            Codes.Add((ushort)lengthBits);
            CodeBits.Add((byte)lengthBitsCount);
            Codes.Add((ushort)distanceCode);
            Codes.Add((ushort)distanceBits);
            CodeBits.Add((byte)distanceBitsCount);

            LengthWritten += length;
        }

        /// <summary>
        /// Internally write end of block.
        /// </summary>
        internal void WriteEOB()
        {
            Codes.Add(DeflateUtils.EndOfBlockCode);
        }
    }
}
